package com.xu.cloud.common.util;

import com.xu.cloud.common.constant.HttpConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.client.ClientHttpRequestFactorySupplier;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.http.client.BufferingClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Map;
import java.util.Objects;

/**
 * RestTemplate工具类
 *
 * @author xuguan
 * @since 2023/5/31
 */
public class RestTemplateUtil {
    private static final RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
    private static final RestTemplate restTemplate;
    /**
     * 默认请求建立连接超时时间(3秒)
     */
    private static final int CONNECT_TIMEOUT = HttpConstant.CONNECT_TIMEOUT;
    /**
     * 默认请求读取响应超时时间(10秒)
     */
    private static final int READ_TIMEOUT = HttpConstant.READ_TIMEOUT;

    static {
        restTemplate = restTemplateBuilder
                .requestFactory(() -> new BufferingClientHttpRequestFactory(
                        new ClientHttpRequestFactorySupplier().get()))
                .setConnectTimeout(Duration.ofMillis(CONNECT_TIMEOUT))
                .setReadTimeout(Duration.ofMillis(READ_TIMEOUT))
                .additionalInterceptors(new LoggingClientHttpRequestInterceptor())
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
                .defaultHeader(HttpHeaders.CONTENT_ENCODING, StandardCharsets.UTF_8.name())
                .setBufferRequestBody(true)
                .build();
    }

    public static <T> T doGet(String url, Class<T> responseType) {
        return restTemplate.getForObject(url, responseType);
    }

    public static <T> T doGet(String url, @Nullable Map<String, String> params, Class<T> responseType) {
        return restTemplate.getForObject(buildQueryUrl(url, params), responseType);
    }

    public static <T> T doGet(String baseUrl, @Nullable HttpHeaders headers, @Nullable Map<String, String> params, Class<T> responseType) {
        HttpEntity<?> httpEntity = new HttpEntity<>(headers);
        return restTemplate.exchange(buildQueryUrl(baseUrl, params), HttpMethod.GET, httpEntity, responseType).getBody();
    }

    public static <T> T doGet(String url, ParameterizedTypeReference<T> responseType) {
        return doGet(url, null, responseType);
    }

    public static <T> T doGet(String url, @Nullable Map<String, String> params, ParameterizedTypeReference<T> responseType) {
        return doGet(url, null, null, responseType);
    }

    public static <T> T doGet(String baseUrl, @Nullable HttpHeaders headers, @Nullable Map<String, String> params, ParameterizedTypeReference<T> responseType) {
        HttpEntity<?> httpEntity = new HttpEntity<>(headers);
        return restTemplate.exchange(buildQueryUrl(baseUrl, params), HttpMethod.GET, httpEntity, responseType).getBody();
    }

    public static <T> T doPost(String url, Object body, Class<T> responseType) {
        return restTemplate.postForObject(url, body, responseType);
    }

    public static <T> T doPost(String url, HttpHeaders headers, Object body, Class<T> responseType) {
        HttpEntity<Object> httpEntity = new HttpEntity<>(body, headers);
        return restTemplate.postForObject(url, httpEntity, responseType);
    }

    public static <T> T doPost(String url, Object body, ParameterizedTypeReference<T> responseType) {
        return doPost(url, null, body, responseType);
    }

    public static <T> T doPost(String url, @Nullable HttpHeaders headers, Object body, ParameterizedTypeReference<T> responseType) {
        HttpEntity<Object> httpEntity = new HttpEntity<>(body, headers);
        return restTemplate.exchange(url, HttpMethod.POST, httpEntity, responseType).getBody();
    }

    public static String buildQueryUrl(String baseUrl, Map<String, String> params) {
        if (CollectionUtils.isEmpty(params)) {
            return baseUrl;
        }
        UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(baseUrl);

        for (Map.Entry<String, String> entry : params.entrySet()) {
            uriComponentsBuilder.queryParam(entry.getKey(), entry.getValue());
        }

        // 默认utf-8编码
        return uriComponentsBuilder.toUriString();
    }

    @Slf4j
    public static class LoggingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
        @Override
        public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
            beforeLogging(request, body);
            ClientHttpResponse response = null;
            try {
                response = execution.execute(request, body);
                return response;
            } finally {
                if (Objects.isNull(response)) {
                    log.error("响应为空");
                } else {
                    afterLogging(response);
                }
            }
        }

        protected void beforeLogging(HttpRequest request, byte[] body) {
            StringBuilder msg = new StringBuilder();
            msg.append("Before request [")
                    .append(request.getMethodValue()).append(" ")
                    .append(request.getURI())
                    .append(", headers=").append(request.getHeaders())
                    .append(", payload=").append(new String(body, StandardCharsets.UTF_8))
                    .append("]");
            log.info(msg.toString());
        }

        protected void afterLogging(ClientHttpResponse response) throws IOException {
            StringBuilder msg = new StringBuilder();
            msg.append("After request [")
                    .append(", statusCode=").append(response.getStatusCode())
                    .append(", statusText=").append(response.getStatusText())
                    .append(", payload=").append(StreamUtils.copyToString(response.getBody(), StandardCharsets.UTF_8))
                    .append("]");
            log.info(msg.toString());
        }
    }

}
